/*
 * DAOC-TB - Tools for DAoC-Player
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 *
 * CPage2.cpp :  The SKILL-Page
 * =============
 *  Here user can setup his skill
 */

#include "stdafx.h"
#include "Charplan.h"
#include "CPage2.h"
#include "charplandoc.h"
#include "CNumEdit.h"
#include "MainFrm.h"
#include "CDlgRespec.h"




#define LIST_CTRLS_ID    5000

// CPage2-Dialogfeld

IMPLEMENT_DYNAMIC(CPage2, CAPage)
CPage2::CPage2(CWnd* pParent /*=NULL*/)
    : CAPage(CPage2::IDD, pParent)
{
    mn_last_descritpion_level = -1;
    mn_last_descritpion_text = -1;
	mb_use_alternativedisplay = false;
}


CPage2::~CPage2()
{
    DeleteList();
}


void CPage2::DoDataExchange(CDataExchange* pDX)
{
    CAPage::DoDataExchange(pDX);
    DDX_Control(pDX, IDC_LIST1, mwnd_liste);
    DDX_Control(pDX, IDC_EDIT1, mwnd_skillpoints);
    DDX_Control(pDX, IDC_EDIT4, mwnd_skillpointsused);
    DDX_Control(pDX, IDC_EDIT2, mwnd_skillpointsrest);
    DDX_Control(pDX, IDC_LIST2, mwnd_description);
    DDX_Control(pDX, IDC_EDIT3, mwnd_description_text);
    DDX_Control(pDX, IDC_EDIT5, mwnd_skillpointsadditional);
}


BEGIN_MESSAGE_MAP(CPage2, CAPage)
    ON_NOTIFY(NM_CUSTOMDRAW, IDC_LIST1, OnNMCustomdrawList1)
    ON_WM_SIZE()
    ON_NOTIFY(HDN_ENDTRACK, IDC_LIST1, OnHdnEndtrackList1)
    ON_NOTIFY(HDN_BEGINTRACK, IDC_LIST1, OnHdnBegintrackList1)
    ON_NOTIFY(LVN_CELL_POS_CHANGED, IDC_LIST1, OnCellsMoved)
    ON_NOTIFY(LVN_GOT_FOCUS, IDC_LIST1, OnListGotFocus)
    ON_EN_CHANGE(IDC_EDIT5, OnEnChangeEdit5)
    ON_BN_CLICKED(IDC_RESPEC, OnBnClickedRespec)
    ON_NOTIFY(NM_CLICK, IDC_LIST1, &CPage2::OnNMClickList1)
    ON_NOTIFY(HDN_DIVIDERDBLCLICK, 0, &CPage2::OnHdnDividerdblclickList1)
END_MESSAGE_MAP()



// CPage2-Meldungshandler

BOOL CPage2::OnInitDialog()
{
    CAPage::OnInitDialog();

	mb_use_alternativedisplay = 0!=GETOPTIONDEF("NEW_SKILL_DISPLAY",1);

    // setup the list headers
    mwnd_liste.InsertColumn(0,_T("Name"));
    mwnd_liste.InsertColumn(1,_T("Level"));
    mwnd_liste.InsertColumn(2,_T("Skill"));
    mwnd_liste.InsertColumn(3,_T("Auto Train"),LVCFMT_CENTER);

    mwnd_description.InsertColumn(0,_T("name"));
    mwnd_description.InsertColumn(1,_T("val"));

    // change style
    mwnd_liste.SetExtendedStyle(LVS_EX_FULLROWSELECT );
    mwnd_description.SetExtendedStyle(LVS_EX_FULLROWSELECT );

    // Update view
    OnClassChanged();

    OnCarrerModeChanged();

    // Resizer
    static const CResizer::CBorderInfo s_bi[] = {
        {IDC_LIST1, {CResizer::eFixed, IDC_MAIN, CResizer::eLeft},
                    {CResizer::eFixed, IDC_MAIN, CResizer::eTop},
                    {CResizer::eFixed, IDC_MAIN, CResizer::eRight},
                    {CResizer::eFixed, IDC_MAIN, CResizer::eBottom}},
        {IDC_LIST2, {CResizer::eFixed, IDC_LIST1, CResizer::eRight},
                    {CResizer::eFixed, IDC_MAIN, CResizer::eTop},
                    {CResizer::eFixed, IDC_MAIN, CResizer::eRight},
                    {CResizer::eFixed, IDC_MAIN, CResizer::eTop}},
        {IDC_EDIT1, {CResizer::eFixed, IDC_LIST1, CResizer::eRight},
                    {CResizer::eFixed, IDC_MAIN, CResizer::eTop},
                    {CResizer::eFixed, IDC_MAIN, CResizer::eRight},
                    {CResizer::eFixed, IDC_MAIN, CResizer::eTop}},
        {IDC_EDIT2, {CResizer::eFixed, IDC_LIST1, CResizer::eRight},
                    {CResizer::eFixed, IDC_MAIN, CResizer::eTop},
                    {CResizer::eFixed, IDC_MAIN, CResizer::eRight},
                    {CResizer::eFixed, IDC_MAIN, CResizer::eTop}},
        {IDC_EDIT3, {CResizer::eFixed, IDC_LIST1, CResizer::eRight},
                    {CResizer::eFixed, IDC_MAIN, CResizer::eTop},
                    {CResizer::eFixed, IDC_MAIN, CResizer::eRight},
                    {CResizer::eFixed, IDC_MAIN, CResizer::eTop}},
        {IDC_EDIT4, {CResizer::eFixed, IDC_LIST1, CResizer::eRight},
                    {CResizer::eFixed, IDC_MAIN, CResizer::eTop},
                    {CResizer::eFixed, IDC_MAIN, CResizer::eRight},
                    {CResizer::eFixed, IDC_MAIN, CResizer::eTop}},
        {IDC_EDIT5, {CResizer::eFixed, IDC_LIST1, CResizer::eRight},
                    {CResizer::eFixed, IDC_MAIN, CResizer::eTop},
                    {CResizer::eFixed, IDC_MAIN, CResizer::eRight},
                    {CResizer::eFixed, IDC_MAIN, CResizer::eTop}},
        {IDC_STATIC1, {CResizer::eFixed, IDC_LIST1, CResizer::eRight},
                    {CResizer::eFixed, IDC_MAIN, CResizer::eTop},
                    {CResizer::eFixed, IDC_MAIN, CResizer::eRight},
                    {CResizer::eFixed, IDC_MAIN, CResizer::eTop}},
        {IDC_STATIC2, {CResizer::eFixed, IDC_LIST1, CResizer::eRight},
                    {CResizer::eFixed, IDC_MAIN, CResizer::eTop},
                    {CResizer::eFixed, IDC_MAIN, CResizer::eRight},
                    {CResizer::eFixed, IDC_MAIN, CResizer::eTop}},
        {IDC_STATIC3, {CResizer::eFixed, IDC_LIST1, CResizer::eRight},
                    {CResizer::eFixed, IDC_MAIN, CResizer::eTop},
                    {CResizer::eFixed, IDC_MAIN, CResizer::eRight},
                    {CResizer::eFixed, IDC_MAIN, CResizer::eTop}},
        {IDC_STATIC4, {CResizer::eFixed, IDC_LIST1, CResizer::eRight},
                    {CResizer::eFixed, IDC_MAIN, CResizer::eTop},
                    {CResizer::eFixed, IDC_MAIN, CResizer::eRight},
                    {CResizer::eFixed, IDC_MAIN, CResizer::eTop}},
        {IDC_RESPEC, {CResizer::eFixed, IDC_LIST1, CResizer::eRight},
                    {CResizer::eFixed, IDC_MAIN, CResizer::eTop},
                    {CResizer::eFixed, IDC_MAIN, CResizer::eRight},
                    {CResizer::eFixed, IDC_MAIN, CResizer::eTop}},
    };
    m_resizer.Init(m_hWnd, NULL, s_bi, sizeof(s_bi)/sizeof(s_bi[0]));

    return TRUE;  
}



void CPage2::DeleteList()
{
    // delete all controll pointers stored in the list
    for (listdata::iterator t=mdb_lines.begin();t<mdb_lines.end();++t)
    {
        if (t->wnd_edit)
        {
            t->wnd_edit->DestroyWindow();
            delete (t->wnd_edit);
        }
    }
    mdb_lines.clear();
}



/**
    FillList()
        Get all styles/spells
        Fill the list control
**/
void CPage2::FillList()
{
    // make sure list is empty
    DeleteList();


    // helping vars
    CStringArray result_lines;
    CStringArray result_cats;
    IntArray     result_levels;
    IntArray     result_ids;


    // Styles
    gdbinterface.GetListOfStyleLines(result_lines);
    for (int t=0;t<result_lines.GetCount();t++)
    {
        gdbinterface.SelectStyleLine(result_lines[t]);

        // insert style title
        s_list_data style_head;
        style_head.name      = result_lines[t];
        style_head.spec      = result_lines[t];
        style_head.row_type  = TITLE_BASE; // 0-> caption, skill able
        style_head.b_isspell = false;
        style_head.b_isautotrain = gdbinterface.IsStyleLineAutotrain();
        

        mdb_lines.push_back(style_head);

        // get all styles
        gdbinterface.GetListOfStyles(NULL, &result_levels,&result_ids);

        // when there is a style...
        if (result_levels.GetCount()>0)
        {
            // add a name
            s_list_data style;
            style.name      = "Styles";
            style.row_type  = SPELLS_SPEC; // 3-> caption, not skill able
            style.spec      = result_lines[t];
            style.b_isspell = false;
            style.b_isautotrain = false;

            // copy levels
            style.level_list.resize(result_levels.GetCount());
            style.id_list.resize(result_levels.GetCount());
            for (int n=0;n<result_levels.GetCount();n++)
            {
                style.level_list[n]=result_levels[n];
                style.id_list[n]=result_ids[n];
            }

            mdb_lines.push_back(style);
        }
    }


    // Spells
    gdbinterface.GetListOfSpellLines(result_lines);
    for (int t=0;t<result_lines.GetCount();t++)
    {
    
        gdbinterface.SelectSpellLine(result_lines[t]);

        // Base

        // add the caption (eg.: "matter")
        s_list_data spell_base_head;

        spell_base_head.name = gdbinterface.GetSpellLineNameBase();
        spell_base_head.spec = spell_base_head.name;

        if (gdbinterface.GetSkillName() != spell_base_head.name)
            spell_base_head.name.Format(_T("%s (%s)"),gdbinterface.GetSkillName(),gdbinterface.GetSpellLineNameBase());

        spell_base_head.row_type = TITLE_BASE;
        if (!gdbinterface.HasSpecSpells())    spell_base_head.row_type = TITLE_NO_SPEC;

        spell_base_head.b_isspell = true;
        spell_base_head.b_isautotrain = gdbinterface.IsSpellLineAutotrain();
        spell_base_head.itemnr =0;
        spell_base_head.wnd_edit = NULL;

        mdb_lines.push_back(spell_base_head);


        gdbinterface.GetListOfSpellCatsBase(result_cats);
        for (int t2=0;t2<result_cats.GetCount();t2++)
        {
            // add Spell-Cat caption (exa.:"Damage GTAOE")
            s_list_data spell;
            spell.name      = result_cats[t2];
            spell.row_type  = SPELLS_BASE;
            spell.spec      = gdbinterface.GetSpellLineNameBase();
            spell.b_isspell = true;
            spell.b_isautotrain = false;
            spell.itemnr    = 0;
            spell.wnd_edit  = NULL;


            gdbinterface.SelectSpellCatBase(result_cats[t2]);
            gdbinterface.GetListOfSpells(NULL,&result_levels,&result_ids);

            // copy levels
            spell.level_list.resize(result_levels.GetCount());
            spell.id_list.resize(result_levels.GetCount());
            for (int n=0;n<result_levels.GetCount();n++)
            {
                spell.level_list[n]=result_levels[n];
                spell.id_list[n]=result_ids[n];
            }

            mdb_lines.push_back(spell);
        }


        // Spec (only when specs are there)
        if (gdbinterface.HasSpecSpells())
        {
            // add Spec header (exam.: "Matter Spec")
            s_list_data spell_spec_head;
            spell_spec_head.name     = gdbinterface.GetSpellLineNameSpec();
            spell_spec_head.row_type = TITLE_SPEC;
            spell_spec_head.b_isspell = true;
            spell_spec_head.b_isautotrain = false;
            if (!spell_spec_head.name.IsEmpty())
                mdb_lines.push_back(spell_spec_head);


            gdbinterface.GetListOfSpellCatsSpec(result_cats);
            for (int t2=0;t2<result_cats.GetCount();t2++)
            {
                // Category title (exam: "DOT")
                s_list_data spell;
                spell.name      = result_cats[t2];
                spell.row_type  = SPELLS_SPEC;
                spell.spec      = gdbinterface.GetSpellLineNameBase();
                spell.b_isspell = true;
                spell.b_isautotrain = false;

                gdbinterface.SelectSpellCatSpec(result_cats[t2]);
                gdbinterface.GetListOfSpells(NULL,&result_levels,&result_ids);

                // copy levels
                spell.level_list.resize(result_levels.GetCount());
                spell.id_list.resize(result_levels.GetCount());
                for (int n=0;n<result_levels.GetCount();n++)
                {
                    spell.level_list[n]=result_levels[n];
                    spell.id_list[n]=result_ids[n];
                }

                mdb_lines.push_back(spell);
            }
        }
    }



    // Fill CListCtrl
    mwnd_liste.DeleteAllItems(); // clear list

    for (size_t t=0;t<mdb_lines.size();t++)
    {
        mdb_lines[t].wnd_edit = NULL;


        // Insert item
        CString name = mdb_lines[t].name;
        if (mdb_lines[t].b_isautotrain)    name+="*";

        mdb_lines[t].itemnr = mwnd_liste.InsertItem(LVIF_TEXT,
                                    mwnd_liste.GetItemCount(),name,
                                    0,0,0,0);

        mwnd_liste.SetItemData(mdb_lines[t].itemnr,t);

        // calculate ItemData
        switch (mdb_lines[t].row_type)
        {
            case SPELLS_BASE:   SetItemLevel(t,(int)GetDocument()->GetLevel()); break;      // Base
            case SPELLS_SPEC:   SetItemLevel(t,GetDocument()->GetSpecLevel(mdb_lines[t].spec)); break; // Spec
        }



        // set level (for LVSCW_AUTOSIZE)
        CString text;
        CString temp;
        for (vector<int>::iterator it=mdb_lines[t].level_list.begin();it!=mdb_lines[t].level_list.end();++it)
        {
            temp.Format("%i ",*it);
            if (mb_use_alternativedisplay) temp +=" ";
            text += temp;
        }
        mwnd_liste.SetItem(mdb_lines[t].itemnr,1,LVIF_TEXT,text,0,0,0,NULL);

        // Find Highest-Spell Effect
        if ((mdb_lines[t].row_type == SPELLS_BASE || mdb_lines[t].row_type == SPELLS_SPEC) && mdb_lines[t].b_isspell)
        {
            // select spell-line and spell-cat
            gdbinterface.SelectSpellLine(mdb_lines[t].spec);
            int level = 0;
            if (mdb_lines[t].row_type == SPELLS_BASE)
            {
                level = (int)GetDocument()->GetLevel(); 
                gdbinterface.SelectSpellCatBase(mdb_lines[t].name);
            }
            else
            {
                level = GetDocument()->GetSpecLevel(mdb_lines[t].spec);
                gdbinterface.SelectSpellCatSpec(mdb_lines[t].name);
            }

            CString effect = gdbinterface.GetHighestSpellEffect(level);
            mwnd_liste.SetItem(mdb_lines[t].itemnr,2,LVIF_TEXT,effect,0,0,0,NULL);
        }

        if (mdb_lines[t].row_type == TITLE_BASE && mdb_lines[t].b_isautotrain)
        {
            int lvl = ((int)GetDocument()->GetLevel())/4;
            CString temp;
            temp.Format(_T("%i lvl"),lvl);
            mwnd_liste.SetItem(mdb_lines[t].itemnr,3,LVIF_TEXT,temp,0,0,0,NULL);
        }
    }

    // set columns width
    mwnd_liste.SetColumnWidth(0,LVSCW_AUTOSIZE);
    mwnd_liste.SetColumnWidth(1,LVSCW_AUTOSIZE);
    mwnd_liste.SetColumnWidth(2,100);
    mwnd_liste.SetColumnWidth(3,70);
    ResizeColumns();

    // Create EDIT Controls
    for (size_t t=0;t<mdb_lines.size();t++)
        if (mdb_lines[t].row_type == TITLE_BASE)
        {
            // SKILL
            CRect r;
            GetCellRect(mdb_lines[t].itemnr,2,LVIR_BOUNDS,r);
            mdb_lines[t].wnd_edit = new CNumEdit;
            mdb_lines[t].wnd_edit->Create( WS_BORDER |WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_NUMBER,
                                            r,&mwnd_liste,LIST_CTRLS_ID+t);

            if (GetDocument()->IsCarrerMode() && GetDocument()->IsSpecced(mdb_lines[t].spec))
                mdb_lines[t].wnd_edit->SetBackground(COLOR_KEYSET);

            mdb_lines[t].wnd_edit->SetValueSilent(GetDocument()->GetSpecLevel(mdb_lines[t].spec));
        }

}



/**
 On scrolling -> Hide all controls
**/
void CPage2::OnHdnBegintrackList1(NMHDR *, LRESULT *pResult)
{
    for (listdata::iterator t=mdb_lines.begin();t<mdb_lines.end();++t)
        if (t->row_type == TITLE_BASE)
        {
            ASSERT(t->wnd_edit);
            t->wnd_edit->SetRedraw(false);
        }
        
    *pResult = 0;
}

/**
 reposition controls
**/
void CPage2::OnHdnEndtrackList1(NMHDR *pNMHDR, LRESULT *pResult)
{
    LPNMHEADER phdr = reinterpret_cast<LPNMHEADER>(pNMHDR);

    // since cells are still on old pos we've to calculate the movement
    int begin_dis=0;
    int end_dis=0;

    if (phdr->iItem<2)
    {
        CRect r;
        GetCellRect(0,phdr->iItem,LVIR_BOUNDS,r);
        begin_dis = phdr->pitem->cxy - r.Width();
    }
    
    if (phdr->iItem==2)
    {
        CRect r;
        GetCellRect(0,2,LVIR_BOUNDS,r);
        end_dis = phdr->pitem->cxy - r.Width();
    }

    UpdateControlPositions(begin_dis,end_dis);

    // reset level positions (recalc on redraw)
    for (listdata::iterator it=mdb_lines.begin();it<mdb_lines.end();++it)
        it->level_pos.clear();


    *pResult = 0;
}



void CPage2::OnHdnDividerdblclickList1(NMHDR *pNMHDR, LRESULT *pResult)
{
    LPNMHEADER phdr = reinterpret_cast<LPNMHEADER>(pNMHDR);
    UpdateControlPositions();

    // reset level positions (recalc on redraw)
    for (listdata::iterator it=mdb_lines.begin();it<mdb_lines.end();++it)
        it->level_pos.clear();
    *pResult = 0;
}


/**
On end scrolling -> reposition controls and show
**/
void CPage2::OnCellsMoved(NMHDR *pNMHDR, LRESULT *pResult)
{
    UpdateControlPositions();
    *pResult = 0;
}

void    CPage2::UpdateControlPositions(int begin_dis, int end_dis)
{
    CRect client;
    CRect header;
    mwnd_liste.GetClientRect(client);
    mwnd_liste.GetHeaderCtrl()->GetWindowRect(header);
    client.top+=header.Height()+2;

    for (listdata::iterator it=mdb_lines.begin();it<mdb_lines.end();++it)
        if (it->row_type == TITLE_BASE)
        {
            CRect r;
            GetCellRect(it->itemnr,2,LVIR_BOUNDS,r);
            r.left  +=begin_dis;
            r.right +=begin_dis+end_dis;
            it->wnd_edit->MoveWindow(r,true);

            if ((r&client).IsRectEmpty())    
                it->wnd_edit->ShowWindow(SW_HIDE);
            else 
            {
                ItemSubInvalidate(it->itemnr,2,FALSE);
                it->wnd_edit->ShowWindow(SW_SHOW);
                it->wnd_edit->SetWindowPos(NULL,r.left,r.top,r.Width(),r.Height(),SWP_NOZORDER);
                it->wnd_edit->SetRedraw(true);
                it->wnd_edit->Invalidate();
            }
        }
}




void CPage2::ItemSubInvalidate(int nr, int subitem, BOOL erase /*= TRUE*/)
{
    CRect r;
    if (!GetCellRect(nr,subitem,LVIR_BOUNDS,r)) return;
    mwnd_liste.InvalidateRect(r,erase);
}

void CPage2::ItemInvalidate(int nr, BOOL erase /*= TRUE*/)
{
    CRect r;
    if (mwnd_liste.GetItemRect(nr,r,LVIR_BOUNDS) == 0) return;
    mwnd_liste.InvalidateRect(r,erase);
}



void CPage2::SkillPointsChanged(bool b_through_level_change /*= false*/)
{
    CString temp;

    // Get Points
    int used = GetDocument()->CalcUsedSkillPoints();
    int all = GetDocument()->GetSkillPoints();

    // Output values
    temp.Format(_T("%i"),used);
    mwnd_skillpointsused.SetWindowText(temp);

    temp.Format(_T("%i"),all);
    mwnd_skillpoints.SetWindowText(temp);

    temp.Format(_T("%i"),all-used);
    if (all-used<0)    mwnd_skillpointsrest.SetTextColor(RGB(255,0,0));
    else            mwnd_skillpointsrest.SetColorsDefault();
    mwnd_skillpointsrest.SetWindowText(temp);


    // Update max Spell-Effect
    BOOL show_sp = GETOPTION(_T("SHOW_SKILL_POINTS"));
    for (listdata::iterator it=mdb_lines.begin();it<mdb_lines.end();++it)
    {
        // Find Highest-Spell Effect for all spec-spells
        if (it->row_type == SPELLS_SPEC && it->b_isspell && !b_through_level_change)
        {
            // select spellline and spellcat
            gdbinterface.SelectSpellLine(it->spec);
            gdbinterface.SelectSpellCatSpec(it->name);
            int level = GetDocument()->GetSpecLevel(it->spec);

            CString effect = gdbinterface.GetHighestSpellEffect(level);
            CString old = mwnd_liste.GetItemText(it->itemnr,2);
            if (old != effect)
                mwnd_liste.SetItemText(it->itemnr,2,effect);
        }
        else
        // show used points
        if (it->row_type == TITLE_BASE && show_sp)
        {
            int level = GetDocument()->GetSpecLevel(it->spec);

            int lvl = 1;
            if (it->b_isautotrain)     lvl = ((int)GetDocument()->GetLevel())/4;

            int p = 0;
            for (int t1=lvl;t1<level;t1++) p+=(t1+1);
            CString temp;
            temp.Format(_T("%i sp"),p);
            mwnd_liste.SetItemText(it->itemnr,1,temp);
        }
    }

/*
    // Invalidate
    for (int t=0;t<mdb_lines.GetCount();t++)
        if (mdb_lines[t].type==3 && mdb_lines[t].b_isspell && !b_through_level_change)
            ItemSubInvalidate(mdb_lines[t].itemnr,2,FALSE);
*/

    CalcMaxSkills();
}



void CPage2::OnClassChanged()
{
    mn_last_descritpion_level = -1;
    mn_last_descritpion_text = -1;

    mwnd_description.DeleteAllItems();
    mwnd_description_text.SetWindowText(CString(""));

    CString text;
    text.Format(_T("%i"),GetDocument()->GetAdditionSkillPoints());
    mwnd_skillpointsadditional.SetWindowText(text);

    // Update all
    FillList();
    SkillPointsChanged();
}



void CPage2::OnLevelChanged()
{
    // find level depending skills
    int level = (int)GetDocument()->GetLevel();

    BOOL b_visi = IsWindowVisible();

    if (b_visi)
        SetRedraw(FALSE);



    for (size_t t=0;t<mdb_lines.size();t++)
    {
        // non-spec spells (set color + get effect)
        if (mdb_lines[t].row_type == SPELLS_BASE)
        {
            // change level
            SetItemLevel(t,level);

            // select spellline and spellcat
            gdbinterface.SelectSpellLine(mdb_lines[t].spec);
            gdbinterface.SelectSpellCatBase(mdb_lines[t].name);
            CString effect = gdbinterface.GetHighestSpellEffect(level);
            mwnd_liste.SetItemText(mdb_lines[t].itemnr,2,effect);
        }


        if (mdb_lines[t].row_type == TITLE_BASE && mdb_lines[t].b_isautotrain)
        {
            int lvl = ((int)GetDocument()->GetLevel())/4;
            CString temp;
            temp.Format(_T("%i lvl"),lvl);
            mwnd_liste.SetItem(mdb_lines[t].itemnr,3,LVIF_TEXT,temp,0,0,0,NULL);
        }
    }




    if (b_visi)
    {
        SetRedraw();
    }


    // invalidate
    for (listdata::iterator it=mdb_lines.begin();it<mdb_lines.end();++it)
    {
        if (it->row_type == SPELLS_BASE)
        {
            // change level and invalidate
            ItemSubInvalidate(it->itemnr,1,FALSE);
            ItemSubInvalidate(it->itemnr,2,FALSE);
        }

        if (it->row_type == TITLE_BASE && it->b_isautotrain)
                ItemSubInvalidate(it->itemnr,3,FALSE);
    }


    // Maik Jurkait [7.2.2005]
    //===========================================
    // update cap-views
    //===========================================
    OnRRChanged();


    // in carrer mode...we have to update the spec-skills to
    if (GetDocument()->IsCarrerMode())
    {
        LevelChangedInCarrerMode();
    }



    SkillPointsChanged(true);

}

void CPage2::OnCarrerModeChanged()
{
    if (GetDocument()->IsCarrerMode())
    {
        for (listdata::iterator it=mdb_lines.begin();it<mdb_lines.end();++it)
            if (it->row_type == TITLE_BASE)
                it->wnd_edit->ShowRangeColors(true);

        LevelChangedInCarrerMode();
    }
    else
    {
        // reset colors
        for (listdata::iterator it=mdb_lines.begin();it<mdb_lines.end();++it)
            if (it->row_type == TITLE_BASE)
            {
                it->wnd_edit->ShowRangeColors(false);
                it->wnd_edit->SetColorsDefault();
                it->wnd_edit->Invalidate(FALSE);
            }
    }
}


/**
*   Calc and set (editbox) max Skill-Levels
**/
void    CPage2::CalcMaxSkills()
{

    // Get Points
    int all = GetDocument()->GetSkillPoints();
    int max_autottrain = (int)GetDocument()->GetLevel()/4;


    // Maik Jurkait [7/21/2007]
    // ------------------------
    //  normal mode
    // ------------------------
    if (!GetDocument()->IsCarrerMode())
    {
        int used = GetDocument()->CalcUsedSkillPoints();

        for (listdata::iterator it=mdb_lines.begin();it<mdb_lines.end();++it)
        {
            if (it->row_type == TITLE_BASE)
            {
                // check autotrain
                int mauto = 1;
                if (it->b_isautotrain)
                    mauto = max(1,max_autottrain);

                // 
                int maxskill = max(mauto, CalcMaxSkill(it->wnd_edit->GetNumber(),all-used) );
                it->wnd_edit->SetRange(1,maxskill);

                // update specs
                for (listdata::iterator st=mdb_lines.begin();st<mdb_lines.end();++st)
                {
                    if (st->row_type == SPELLS_SPEC && st->spec == it->spec)
                    {
                        if (st->tint_toohigh != maxskill)
                        {
                            st->tint_toohigh = maxskill;
                            ItemSubInvalidate(st->itemnr,1,FALSE);
                        }
                    }
                }
            }
        }

        return;
    }
    
    // Maik Jurkait [7/21/2007]
    // ------------------------
    // carrer mode
    // ------------------------
    int min_used_points = 0;

    // calculate minimal used skillpoints
    int vpremin,vmin,vmax;
    for (listdata::iterator it=mdb_lines.begin();it<mdb_lines.end();++it)
        if (it->row_type == TITLE_BASE)
        {
            GetDocument()->GetCarrerSpecMinMaxLevel(it->spec,vpremin,vmin,vmax);
            TRACE3("%s - %i-%i",it->spec,vpremin,vmax);

            // calc used skill points
            int l = 1;
            if (it->b_isautotrain)
                l = max(1,max_autottrain);
            while (l<vmin)  min_used_points += (l++)+1;
        }


    for (listdata::iterator it=mdb_lines.begin();it<mdb_lines.end();++it)
        if (it->row_type == TITLE_BASE)
        {
            GetDocument()->GetCarrerSpecMinMaxLevel(it->spec,vpremin,vmin,vmax);

            // credit possible minimum
            int min_used_temp = min_used_points;
            while (vmin>vpremin)
            {
                // autotrain (no credit)
                if (it->b_isautotrain && vmin<=max_autottrain) break;

                min_used_temp -= (vmin--);
            }

            // calc possible max
            vmax = min (vmax,CalcMaxSkill(vmin,all-min_used_temp));

            // autotrain
            if (it->b_isautotrain)
                vmax = max(vmax,max_autottrain);


            it->wnd_edit->SetRange(vpremin,vmax);
        }
}



void    CPage2::LevelChangedInCarrerMode()
{
    ASSERT(GetDocument()->IsCarrerMode());

    // Calc and set max Skill-Levels
    for (size_t t=0;t<mdb_lines.size();t++)
    {

        if (mdb_lines[t].row_type == TITLE_BASE)
        {
            int level = GetDocument()->GetSpecLevel(mdb_lines[t].spec);
            mdb_lines[t].wnd_edit->SetRange(1,80); // disable range
            mdb_lines[t].wnd_edit->SetValueSilent(level);
            if (GetDocument()->IsSpecced(mdb_lines[t].spec))
                mdb_lines[t].wnd_edit->SetBackground(COLOR_KEYSET);
            else
                mdb_lines[t].wnd_edit->SetColorsDefault();
        }

        // Find Highest-Spell Effect for all spec-spells
        if (mdb_lines[t].row_type == SPELLS_SPEC)
        {
            int level = GetDocument()->GetSpecLevel(mdb_lines[t].spec);
            SetItemLevel(t,level);

            if (mdb_lines[t].b_isspell)
            {
                // select spellline and spellcat
                gdbinterface.SelectSpellLine(mdb_lines[t].spec);
                gdbinterface.SelectSpellCatSpec(mdb_lines[t].name);

                CString effect = gdbinterface.GetHighestSpellEffect(level);
                mwnd_liste.SetItem(mdb_lines[t].itemnr,2,LVIF_TEXT,effect,0,0,0,NULL);
            }
        }
    }

    // update range
    CalcMaxSkills();
}



void CPage2::CalculatePositions(CDC *pDC, const CRect& rc, s_list_data* data	)
{
    CString text;
    CSize size;

	data->level_pos.resize(data->level_list.size());


	if (mb_use_alternativedisplay)
	{
		// alternative positioning
		const int border=2;
        int totalw=0;
		int minx = pDC->GetTextExtent(_T("1")).cx/2;
        int width = rc.Width() -minx - pDC->GetTextExtent(_T("50")).cx/2;
		data->relative_width=rc.Width() -pDC->GetTextExtent(_T("50")).cx/2;
		
        for (size_t t = 0; t<data->level_list.size();++t)
        {
            text.Format("%i",data->level_list[t]);
            CSize c = pDC->GetTextExtent(text);
            int p = minx-c.cx/2 + (data->level_list[t]-1)*width / 49;
            data->level_pos[t]= CSize(p,p+c.cx);
            totalw += c.cx+border*2;
		}

        // try to move overlappings
        int moves = 10; // prevent infinitive loops
        int mborder = border;
        size_t count = data->level_pos.size();

        if (count>0)
        {
            do
            {
                // move right
                bool b_no_overlap=true;
                for (size_t t=1;t<count;++t)
                {
                    if (data->level_pos[t-1].cy+mborder > data->level_pos[t].cx)
                    {
                        b_no_overlap=false;
                        int d = data->level_pos[t-1].cy - data->level_pos[t].cx + mborder;

                        if (t+1==count) // last element->don't leave rect
                        {
                            if (data->level_pos[t].cy+d >= rc.Width())
                                d = rc.Width()-data->level_pos[t].cy;
                        }
                                    
                        data->level_pos[t].cx +=d;
                        data->level_pos[t].cy +=d;
                    }
                }

                if (b_no_overlap) break;

                // move left
                b_no_overlap=true;
                for (int t=count-2;t>=0;--t)
                {
                    if (data->level_pos[t].cy+mborder > data->level_pos[t+1].cx)
                    {
                        b_no_overlap=false;
                        int d = data->level_pos[t+1].cx - data->level_pos[t].cy - mborder;

                        if (t==0)
                        {
                            if (data->level_pos[t].cx+d < 0)
                                d = -data->level_pos[t].cx;
                        }

                        data->level_pos[t].cx +=d;
                        data->level_pos[t].cy +=d;
                    }
                }
                if (b_no_overlap) break;
                            
                moves--;
                if (moves<6) mborder=1;
                if (moves<3) mborder=0;

            } while (moves>0);
            if (!moves)
            {
                TRACE("unable to set positions for %s",data->name);
            }
        }
	}
	else
	{
        int x = 0;
        for (size_t t = 0; t<data->level_list.size();++t)
        {
            text.Format("%i",data->level_list[t]);
            size = pDC->GetTextExtent(text);
            data->level_pos[t]=CSize(x,x+size.cx);
            x += size.cx+3;
        }
	}
}


/**
    the CListCtrl drawing routing
**/
void CPage2::OnNMCustomdrawList1(NMHDR *pNMHDR, LRESULT *pResult)
{
    LPNMLVCUSTOMDRAW  lplvcd = (LPNMLVCUSTOMDRAW)pNMHDR;

    switch(lplvcd->nmcd.dwDrawStage)
    {
        case CDDS_PREPAINT:
            *pResult = CDRF_NOTIFYSUBITEMDRAW; // ask for item notifications.
            break;

        case CDDS_ITEMPREPAINT:
            *pResult = CDRF_NOTIFYSUBITEMDRAW; // ask for sub item notifications
            break;

        // Cell drawing routine
        case CDDS_ITEMPREPAINT|CDDS_SUBITEM:
        {
                *pResult = CDRF_DODEFAULT;

                int iCol = lplvcd->iSubItem;
                int iRow = (int)lplvcd->nmcd.dwItemSpec;

                s_list_data* data = &mdb_lines[lplvcd->nmcd.lItemlParam];

                // Check color
                lplvcd->clrTextBk = RGB(255,255,255);
                switch (data->row_type)
                {
                    case TITLE_BASE:
                        if (iCol == 2)
                        {
                            *pResult = CDRF_SKIPDEFAULT;
                            return; // don't draw background under controls
                        }
                        lplvcd->clrTextBk = RGB(196, 196, 255);
                        return;

                    case TITLE_SPEC:
                        lplvcd->clrTextBk = RGB(212, 212, 212);
                        return;
                    case TITLE_NO_SPEC:
                        lplvcd->clrTextBk = RGB(196, 196, 255);
                        break;
                }


                // if not column 1 we can leave and let control do the default drawing
                if (iCol != 1) break;

                // get cell coors
                CRect rc;
                GetCellRect(iRow, iCol, LVIR_BOUNDS, rc);

                // get the device context.
                CDC *pDC= CDC::FromHandle(lplvcd->nmcd.hdc);

                // clear the background
                pDC->FillSolidRect(rc,lplvcd->clrTextBk);

                // just add a little space to the right and left
                rc.left+=3;
                rc.right-=3;

                // recalculate position
                if (data->level_pos.size()!=data->level_list.size())
                {
					CalculatePositions(pDC, rc, data);
				}



				// alternative display decoration
				if (mb_use_alternativedisplay)
                {
                    // find the levels below and above the tint level
                    int x1l=1; int x1p=0;
                    int x2l=80; int x2p=data->relative_width;
                    for (size_t t=0;t<data->level_list.size();++t)
                    {
                        int l = data->level_list[t];
                        if (l <= data->tint_level && l > x1l)   { x1l = l; x1p = (data->level_pos[t].cx+data->level_pos[t].cy)/2;}
                        if (l >  data->tint_level && l < x2l)   { x2l = l; x2p = (data->level_pos[t].cx+data->level_pos[t].cy)/2;}
                    }
                    if (x2l==x1l) x2l=x1l+1;
                    int x = rc.left + x1p+ (data->tint_level-x1l) * (x2p-x1p) / (x2l-x1l);

    				CPen pen(PS_SOLID,0,RGB(212,212,212));
					CPen* old = pDC->SelectObject(&pen);

                    pDC->MoveTo(x,rc.top);
                    pDC->LineTo(x,rc.bottom);
                    CRect s = rc;
                    s.right=x;
                    pDC->FillSolidRect(s,RGB(250,250,255));

					// dots
					if (data->level_pos.size()>0)
					{
           				CPen pen(PS_DOT,0,RGB(164,164,164));
                        pDC->SelectObject(&pen);

						int x = rc.left;
                    	for (vector<CSize>::iterator it=data->level_pos.begin();it!=data->level_pos.end();++it)
						{
							int y = rc.left+it->cx-2;
							if (y>x)
							{
								pDC->MoveTo(x,rc.bottom-4);
								pDC->LineTo(y,rc.bottom-4);
							}
							x = rc.left+it->cy+2;
						}
                    
					}

    				pDC->SelectObject(old);
                }



                // draw all levels
                CString text;
                int base_x = rc.left;

                
                // draw levels
                vector<CSize>::iterator it_pos=data->level_pos.begin();
                vector<int>::iterator   it_ids=data->id_list.begin();
                vector<int>::iterator   it_lvl=data->level_list.begin();
                for (;it_lvl!=data->level_list.end();++it_lvl,++it_ids,++it_pos)
                {
                    if (*it_ids == data->tint_hilight)      pDC->SetTextColor(RGB(0,164,0));
                    else if (*it_lvl <= data->tint_level)   pDC->SetTextColor(RGB(255,0,0));
                    else if (*it_lvl <= data->tint_cap)     pDC->SetTextColor(RGB(164,164,255));
                    else if (*it_lvl > data->tint_toohigh)  pDC->SetTextColor(RGB(164,164,164));
                    else                                    pDC->SetTextColor(RGB(0,0,0));

                    text.Format("%i ",*it_lvl);

                    rc.left = base_x + it_pos->cx;
                    pDC->DrawText(text , rc, DT_LEFT);
                }

                *pResult= CDRF_SKIPDEFAULT;
                break;

            }
            break;

        default:
        *pResult = CDRF_DODEFAULT;
    }
}


/**
 Calc rectangle of a specific cell in the listview
**/
BOOL CPage2::GetCellRect(int iRow, int iCol, int nArea, CRect &rect)
{
     if(iCol)
        return mwnd_liste.GetSubItemRect(iRow, iCol, nArea, rect);


    iCol = 1;
    CRect rCol1;
    if(!mwnd_liste.GetSubItemRect(iRow, iCol, nArea, rCol1))
        return FALSE;

    if(!mwnd_liste.GetItemRect(iRow, rect, nArea))
        return FALSE;

    rect.right = rCol1.left;

    return TRUE;
}




/**
 Recalc layout
**/
void CPage2::OnSize(UINT nType, int cx, int cy)
{
    CAPage::OnSize(nType, cx, cy);

    if (IsWindow(mwnd_liste.m_hWnd))
    {
        ResizeColumns();
        UpdateControlPositions();
    }
}


void CPage2::ResizeColumns()
{
	if (mb_use_alternativedisplay)
    {
        // set columns width
        CRect r; mwnd_liste.GetClientRect(r); 
        int wt = r.Width();
        int w1=mwnd_liste.GetColumnWidth(0);
        int w2=mwnd_liste.GetColumnWidth(1);
        wt -= (w1+w2+100+70);
        mwnd_liste.SetColumnWidth(2,100+wt/10);
        mwnd_liste.Invalidate();
    }

    // reset level positions (recalc on redraw)
    for (listdata::iterator it=mdb_lines.begin();it<mdb_lines.end();++it)
        it->level_pos.clear();
}


/**
* Handle Skill-Level Changed messages from the controlls inside the clistctrl
**/
BOOL CPage2::OnCommand(WPARAM wParam, LPARAM lParam)
{
    if (HIWORD(wParam) == EN_CHANGE)
    {
        int id = LOWORD(wParam)-LIST_CTRLS_ID;
        if (id >=  0 && id < (int)mdb_lines.size())
        {
            int n = mdb_lines[id].wnd_edit->GetNumber();
            ChangeSpec(mdb_lines[id].spec,n);

            if (GetDocument()->IsCarrerMode())
                mdb_lines[id].wnd_edit->SetBackground(COLOR_KEYSET);

            return TRUE;
        }
    }

    return CAPage::OnCommand(wParam, lParam);
}




/**
    Change the skill-level and update the view
**/
void CPage2::ChangeSpec(const CString& str, int level)
{
    GetDocument()->SetSpecLevel(str,level);

    for (size_t t=0;t<mdb_lines.size();t++)
        if (mdb_lines[t].row_type == SPELLS_SPEC && mdb_lines[t].spec == str)
        {
            SetItemLevel(t,level);

            ItemSubInvalidate(mdb_lines[t].itemnr,1,FALSE);
        }

    if (GetDocument()->IsCarrerMode())
    {
        LevelChangedInCarrerMode();
    }

    SkillPointsChanged();
}


int     CPage2::CalcMaxSkill(int curlevel, int avalibepoints)
{
    int maxlevel = (int)GetDocument()->GetLevel();
    int level;
    for (level=curlevel; level<maxlevel && (avalibepoints>level);level++)
        avalibepoints -= level+1;

    return level;
}


int     CPage2::CalcIDAtPos(const s_list_data& data, int x_coor)
{
    x_coor -=3;
    for (size_t t=0;t<data.level_pos.size();++t)
    {
        if (x_coor-1 >= data.level_pos[t].cx && x_coor+1 <= data.level_pos[t].cy)
            return data.id_list[t];
    }

    return 0;
}


void CPage2::PrintSelectedDescriptionDetailID(bool spell, int idx)
{
    if (idx<=0) return;

    CStringArray val,names;

    // select the spell/style
    if (spell)
    {
        gdbinterface.SelectSpellID(idx);
        gdbinterface.GetSpellDescription(names,val);
    }
    else
    {
        gdbinterface.SelectStyleID(idx);
        gdbinterface.GetStyleDescription(names,val);

        mwnd_description_text.SetWindowText(gdbinterface.GetStyleDescriptionText());
    }

 
    // print in the second listview
    mwnd_description.SetRedraw(FALSE);
    mwnd_description.DeleteAllItems();
    for (int t=0;t<names.GetCount();t++)
    {
        int n = mwnd_description.InsertItem(mwnd_description.GetItemCount(),names[t]);
        mwnd_description.SetItem(n,1,LVIF_TEXT,val[t],0,0,0,NULL);
    }
    mwnd_description.SetColumnWidth(0,LVSCW_AUTOSIZE);
    mwnd_description.SetColumnWidth(1,LVSCW_AUTOSIZE);
    mwnd_description.SetRedraw(TRUE);
    mwnd_description.Invalidate();
}


/**
    Track and generate the quick-info for each spell/style the mouse is over
**/
BOOL CPage2::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{

    // Maik Jurkait  23.06.2004 12:16:47
    // ------------------------------------------
    //    Only accept mouse-moves
    // ------------------------------------------
    if (message != LVN_MOUSEMOVE)
        return CAPage::OnWndMsg(message, wParam, lParam, pResult);


    // Maik Jurkait  23.06.2004 12:19:43
    // ------------------------------------------
    //    only if we are in a "level" cell
    //    check if this is a header
    // ------------------------------------------
    int item = LOWORD(wParam);
    if (item == -1)    
        return TRUE;

	const s_list_data& skill_line = mdb_lines[item];
    if (skill_line.row_type != SPELLS_BASE && skill_line.row_type != SPELLS_SPEC ) 
    {
        // clear hilit
        if (mn_last_descritpion_text!=-1)
            SetItemHilit(mn_last_descritpion_text,0);

        mn_last_descritpion_level = -1;
        return TRUE;
    }


    // Maik Jurkait [12.12.2004]
    //===========================================
    // Only if this is the active document
    //===========================================
    CFrameWnd *pWnd = (CFrameWnd *) AfxGetMainWnd();  
    ASSERT(pWnd->IsKindOf(RUNTIME_CLASS(CFrameWnd)));
    pWnd = pWnd->GetActiveFrame();
    if (pWnd && pWnd->GetActiveDocument() != GetDocument()) return TRUE;


    // Maik Jurkait  23.06.2004 12:18:14
    // ------------------------------------------
    //    Calculate cell position
    // ------------------------------------------
    int subitem;
    int x_coor = LOWORD(lParam);


    // find sub item
    CHeaderCtrl*head = mwnd_liste.GetHeaderCtrl();
    CRect r;
    for (subitem=0;subitem<head->GetItemCount();subitem++)
    {
        head->GetItemRect(subitem,r);
        if (r.left<=x_coor && r.right>=x_coor) break;
    }
    x_coor -= r.left;


	// Set level if mouse button is down
	if (mb_use_alternativedisplay && (subitem == 1) && (HIWORD(lParam) &  MK_LBUTTON))
	{
		OnSetSkillByPos(skill_line, x_coor);
	}


    // if it is SPELL
    if (skill_line.b_isspell)
    {
        // select spell-line and spell-cat
        gdbinterface.SelectSpellLine(skill_line.spec);
        if (skill_line.row_type == SPELLS_BASE)
            gdbinterface.SelectSpellCatBase(skill_line.name);
        else
            gdbinterface.SelectSpellCatSpec(skill_line.name);

        // General
        if (item != mn_last_descritpion_text)
        {
            if (mn_last_descritpion_text>=0)
            {
                // set highlight
                SetItemHilit(mn_last_descritpion_text,0);
            }

            mwnd_description_text.SetWindowText(gdbinterface.GetSpellDescriptionText());
            mn_last_descritpion_text = item;
            mn_last_descritpion_level = -1;
        }


        // Details
        if (subitem == 1)
        {
            int idx = CalcIDAtPos(skill_line, x_coor);
            if (idx!= mn_last_descritpion_level)
            {
                PrintSelectedDescriptionDetailID(true, idx);
                mn_last_descritpion_level = idx;

                SetItemHilit(item,idx);
            }
        }
        else
        {
            // clear hilit
            SetItemHilit(mn_last_descritpion_text,0);
            mn_last_descritpion_level = -1;
        }

    }
    else // so its a STYLE
    {
        gdbinterface.SelectStyleLine(skill_line.spec);

        // change hilit
        if (item != mn_last_descritpion_text)
        {
            if (mn_last_descritpion_text>=0)
                SetItemHilit(mn_last_descritpion_text,0);

            mn_last_descritpion_text = item;
            mn_last_descritpion_level = -1;
        }

        // Details
        if (subitem == 1)
        {
            int idx = CalcIDAtPos(skill_line, x_coor);
            if (idx!= mn_last_descritpion_level)
            {
                PrintSelectedDescriptionDetailID(false, idx);
                mn_last_descritpion_level = idx;

                SetItemHilit(item,idx);
            }
        }
        else
        {
            // clear hilit
            SetItemHilit(mn_last_descritpion_text,0);
            mn_last_descritpion_level = -1;
        }

    }

    return TRUE;
}


void CPage2::OnNMClickList1(NMHDR *pNMHDR, LRESULT *pResult)
{
    LPNMITEMACTIVATE pdata = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
    *pResult = 0;

	// only handle skill-bar and valid clics
	if (pdata->iSubItem!=1 || pdata->iItem<0 || (unsigned)pdata->iItem>= mdb_lines.size()) return;

	// calculate reall x_coor
	int x_coor = pdata->ptAction.x;
	CHeaderCtrl*head = mwnd_liste.GetHeaderCtrl();
    CRect r;
    for (int subitem=0;subitem<head->GetItemCount();++subitem)
    {
        head->GetItemRect(subitem,r);
        if (r.left<=x_coor && r.right>=x_coor) break;
    }
	x_coor -= (3+r.left);


	// find and set level
	OnSetSkillByPos(mdb_lines[pdata->iItem], x_coor);
}


void CPage2::OnSetSkillByPos(const s_list_data& data, int x_coor)
{
	int lower=1; 
	int lower_pos=0;
	int higher=80;
	int higher_pos=data.relative_width;
    for (size_t t=0;t<data.level_pos.size();++t)
    {
        if (x_coor-1 >= data.level_pos[t].cx)
			if (lower<data.level_list[t]) 
			{
				lower=data.level_list[t];
				lower_pos=(data.level_pos[t].cx+data.level_pos[t].cy)/2;
			}
		if (x_coor+1 <= data.level_pos[t].cy)
			if (higher>=data.level_list[t])
			{
				higher=data.level_list[t];
				higher_pos=(data.level_pos[t].cx+data.level_pos[t].cy)/2;
			}
				
    }

	// between levels
	if (lower!=higher)
	{
		if (!mb_use_alternativedisplay) return;
        if (higher_pos == lower_pos) higher_pos = lower_pos+1;
		lower = (x_coor-lower_pos)*(higher-lower)/(higher_pos-lower_pos)+lower;
	}

    // find and change control
    for (listdata::iterator it=mdb_lines.begin();it<mdb_lines.end();++it)
    {
        if (it->row_type == TITLE_BASE && data.spec == it->spec)
        {
            ASSERT(it->wnd_edit);
            it->wnd_edit->SetValue(lower);
        }
    }
}


void CPage2::OnEnChangeEdit5()
{
    CString text;
    mwnd_skillpointsadditional.GetWindowText(text);

    int add = _tstoi(text);
    if (add == GetDocument()->GetAdditionSkillPoints()) return;

    GetDocument()->SetAdditionSkillPoints(add);

    SkillPointsChanged();
}





void CPage2::OnBnClickedRespec()
{
    CStringArray result_lines;

    // Maik Jurkait [7.2.2005]
    //===========================================
    // Dialog
    //===========================================
    CDlgRespec dia;
    dia.SetLevel((int)GetDocument()->GetLevel());
    if (dia.DoModal()!=IDOK) return;
    CString respec = dia.GetRespecLine();




    // Maik Jurkait [7.2.2005]
    //===========================================
    // career mode
    //===========================================
    if (GetDocument()->IsCarrerMode())
    {
        // Maik Jurkait [7.2.2005]
        //===========================================
        // Snap key from level before
        //===========================================
        float level = GetDocument()->GetLevel();
        float prelevel = level;
        if (prelevel>40.4) prelevel -= 0.5;
        else prelevel--;
        GetDocument()->SetLevel(prelevel);
        GetDocument()->CarrerSnapKeys();
        GetDocument()->SetLevel(level);
        GetDocument()->SetRespec(level);

        // Styles
        gdbinterface.GetListOfStyleLines(result_lines);
        for (int t=0;t<result_lines.GetCount();t++)
        {
            gdbinterface.SelectStyleLine(result_lines[t]);

            // line respec?
            if (!respec.IsEmpty() && respec != result_lines[t]) continue;


            // Maik Jurkait [7.2.2005]
            //===========================================
            // Set to 1 or if autotrain to current level
            //===========================================
            int level = 1;
            if (gdbinterface.IsStyleLineAutotrain())
            {
                level = (int)(GetDocument()->GetLevel())/4;
            };

            // update number-edit
            for (listdata::iterator it=mdb_lines.begin();it<mdb_lines.end();++it)
            {
                if (it->row_type == TITLE_BASE && it->spec == result_lines[t])
                {
                    ASSERT(it->wnd_edit != NULL);
                    it->wnd_edit->SetValueSilent(level);
                }
            }
            ChangeSpec(result_lines[t],level);
        }


        // Spells
        gdbinterface.GetListOfSpellLines(result_lines);
        for (int t=0;t<result_lines.GetCount();t++)
        {
            // line respec?
            if (!respec.IsEmpty() && respec != result_lines[t]) continue;

            // update number-edit
            for (listdata::iterator it=mdb_lines.begin();it<mdb_lines.end();++it)
            {
                if (it->row_type == TITLE_BASE && it->spec == result_lines[t])
                {
                    ASSERT(it->wnd_edit != NULL);
                    it->wnd_edit->SetValueSilent(1);
                }
            }
            ChangeSpec(result_lines[t],1);
        }
    }

    // Maik Jurkait [7.2.2005]
    //===========================================
    // Normal mode
    //===========================================
    else
    {
        // Styles
        gdbinterface.GetListOfStyleLines(result_lines);
        for (int t=0;t<result_lines.GetCount();t++)
        {
            gdbinterface.SelectStyleLine(result_lines[t]);

            // line respec?
            if (!respec.IsEmpty() && respec != result_lines[t]) continue;


            // Maik Jurkait [7.2.2005]
            //===========================================
            // Set to 1 or if autotrain to current level
            //===========================================
            int level = 1;
            if (gdbinterface.IsStyleLineAutotrain())
            {
                level = (int)(GetDocument()->GetLevel())/4;
            };

            // update number-edit
            for (listdata::iterator it=mdb_lines.begin();it<mdb_lines.end();++it)
            {
                if (it->row_type == TITLE_BASE && it->spec == result_lines[t])
                {
                    ASSERT(it->wnd_edit != NULL);
                    it->wnd_edit->SetValueSilent(level);
                }
            }
            ChangeSpec(result_lines[t],level);
        }


        // Spells
        gdbinterface.GetListOfSpellLines(result_lines);
        for (int t=0;t<result_lines.GetCount();t++)
        {
            // line respec?
            if (!respec.IsEmpty() && respec != result_lines[t]) continue;

            // update number-edit
            for (listdata::iterator it=mdb_lines.begin();it<mdb_lines.end();++it)
            {
                if (it->row_type == TITLE_BASE && it->spec == result_lines[t])
                {
                    ASSERT(it->wnd_edit != NULL);
                    it->wnd_edit->SetValueSilent(1);
                }
            }
            ChangeSpec(result_lines[t],1);
        }
    }
}


void    CPage2::SetItemHilit(int item, int lit)
{
    if (mdb_lines[item].tint_hilight == lit) return;
    mdb_lines[item].tint_hilight = lit;
    ItemSubInvalidate(mdb_lines[item].itemnr,1,FALSE);
}


void    CPage2::SetItemLevel(int item, int level)
{
    int cap = 0;

    if (mdb_lines[item].spec == "Vergiften" || mdb_lines[item].spec == "Player poisons" 
        || mdb_lines[item].spec == "Envenom" )
    {
        cap = level+(int)(GetDocument()->GetLevel())/5 + 1;
        cap += GetDocument()->GetRR()/10;
    }

    mdb_lines[item].tint_toohigh = 80;
    mdb_lines[item].tint_level = level;
    mdb_lines[item].tint_cap = cap;
}



void CPage2::OnRRChanged()
{
    for (size_t t=0;t<mdb_lines.size();t++)
    {
        if (mdb_lines[t].row_type == SPELLS_SPEC &&
                ( mdb_lines[t].spec == "Vergiften" || 
                  mdb_lines[t].spec == "Player poisons"||
                  mdb_lines[t].spec == "Envenom" ))
        {
            int level = GetDocument()->GetSpecLevel(mdb_lines[t].spec);
            SetItemLevel(t,level);
        }
    }
}


void CPage2::OnListGotFocus(NMHDR *pNMHDR, LRESULT *pResult)
{
    for (size_t t=0;t<mdb_lines.size();t++)
    {
        if (mdb_lines[t].wnd_edit && mdb_lines[t].wnd_edit->IsWindowVisible())
        {
            mdb_lines[t].wnd_edit->SetFocus();
            return;
        }
    }
}


